package cRpc

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"

	"github.com/gin-gonic/gin"

	"gitee.com/csingo/cCommon"
	"gitee.com/csingo/cContext"
	"gitee.com/csingo/cError"
	"gitee.com/csingo/cHTTPClient"
	"gitee.com/csingo/cHelper"
	"gitee.com/csingo/cLog"
	"gitee.com/csingo/cResponse"
)

func HTTPRequest(ctx *gin.Context, client RpcClientInterface, method string, req, rsp any) (err error) {
	pkg, app, service := client.RpcClientName()
	host := client_container.GetHost(pkg, app, service)
	uri := rpc_config.RequestURI
	signature := client_container.GetSignature()
	if signature == "" {
		signature = rpc_config.Signature
	}

	// 使用自定义方法获取 rpc 请求 host uri
	if rpc_config.HostFunc != nil {
		host, uri = rpc_config.HostFunc(ctx, app, service)
	}

	var requestBytes []byte
	var body []byte
	var response *cHTTPClient.Response
	var result = &cResponse.ApiData{}

	requestBytes, err = json.Marshal(req)
	if err != nil {
		cLog.WithContext(ctx, map[string]any{
			"source": "cRpc.HTTPRequest",
			"req":    req,
			"err":    err.Error(),
		}).Error("http rpc 请求参数 json 结构化失败")
		return
	}

	matedataJsonBytes, err := json.Marshal(metadata_config.Context)
	if err != nil {
		cLog.WithContext(ctx, map[string]any{
			"source":   "cRpc.HTTPRequest",
			"metadata": metadata_config.Context,
			"err":      err.Error(),
		}).Error("http rpc 结构化 metadata config 失败")
		return
	}

	var metadataContext = make(map[string]any)
	err = json.Unmarshal(matedataJsonBytes, &metadataContext)
	if err != nil {
		cLog.WithContext(ctx, map[string]any{
			"source":   "cRpc.HTTPRequest",
			"metadata": metadata_config.Context,
			"err":      err.Error(),
		}).Error("http rpc 解析 metadata config 失败")
		return
	}

	var metadata = make(map[string]any)
	for k, v := range ctx.Keys {
		switch k.(type) {
		case string:
			kItem := k.(string)
			if _, ok := metadataContext[kItem]; ok {
				metadata[kItem] = v
			}
		}
	}

	contextData, err := json.Marshal(metadata)

	headers := map[string]string{
		"Content-Type":             "application/json",
		cCommon.Header_Rpc_App:     app,
		cCommon.Header_Rpc_Service: service,
		cCommon.Header_Rpc_Method:  method,
		cCommon.Header_Rpc_Context: string(contextData),
		cCommon.XIndex_TraceId:     cContext.GetTraceId(ctx),
	}

	// 透传请求头
	for k := range ctx.Request.Header {
		if _, ok := headers[k]; !ok {
			headers[k] = ctx.GetHeader(k)
		}
	}

	// 设置签名校验
	noSignature := cHelper.ToBool(ctx.GetHeader(cCommon.Header_Rpc_NoSignature))
	if noSignature {
		source := headers[cCommon.XIndex_TraceId]
		headers[cCommon.Header_Rpc_Sign] = cHelper.Md5([]byte(source))
	} else if rpc_config.SignEncryptFunc != nil {
		headers[cCommon.Header_Rpc_Sign] = rpc_config.SignEncryptFunc(ctx, app, service)
	} else if signature != "" {
		source := fmt.Sprintf("%s:%s", signature, headers[cCommon.XIndex_TraceId])
		headers[cCommon.Header_Rpc_Sign] = cHelper.Md5([]byte(source))
	} else {
		source := headers[cCommon.XIndex_TraceId]
		headers[cCommon.Header_Rpc_Sign] = cHelper.Md5([]byte(source))
	}

	option := cHTTPClient.Option{
		Host:    host,
		Uri:     uri,
		Method:  cHTTPClient.MethodPOST,
		Timeout: time.Duration(rpc_config.Timeout) * time.Second,
		Try:     int(rpc_config.Try),
		Headers: headers,
		Data:    string(requestBytes),
	}

	response, err = cHTTPClient.Request(ctx, option)
	if err != nil {
		cLog.WithContext(ctx, map[string]any{
			"source": "cRpc.HTTPRequest",
			"option": option,
			"err":    err.Error(),
		}).Error("http rpc 请求失败")
		return
	}

	err = json.Unmarshal(response.Body, result)
	if err != nil {
		cLog.WithContext(ctx, map[string]any{
			"source": "cRpc.HTTPRequest",
			"err":    err.Error(),
		}).Error("http rpc 请求结果 json 解析失败")
		return
	}

	if result.Code != cError.CError_Csingo_Nil.Code() {
		err = &cError.BaseError{
			ErrCode: result.Code,
			ErrMsg:  strings.TrimLeft(result.Msg, fmt.Sprintf("%s: ", cError.CError_Csingo_System.Error())),
		}
		return
	}

	body, err = json.Marshal(result.Data)
	if err != nil {
		cLog.WithContext(ctx, map[string]any{
			"source": "cRpc.HTTPRequest",
			"err":    err.Error(),
		}).Error("http rpc 请求结果转换 json 结构化失败")
		return
	}

	err = json.Unmarshal(body, rsp)
	if err != nil {
		cLog.WithContext(ctx, map[string]any{
			"source": "cRpc.HTTPRequest",
			"err":    err.Error(),
		}).Error("http rpc 请求结果转换 json 解析失败")
	}

	return
}
